package ex1;

public class ParallelGameOfLife implements GameOfLife {

	public boolean[][] invoke(boolean[][] initalField, int hSplit, int vSplit,
			int generations) {
		if (generations == 0) {
			return initalField;
		}
		
		// Devide board by params
		GOLThread[][] threads = new GOLThread[vSplit][hSplit];
		
		int numOfBigBoardRows = initalField.length;
		int numOfBigBoardColumns = initalField[0].length;
		
		// Number of rows per thread (except for the last which gets all the rest)
		int numOfRowsPerThread = (numOfBigBoardRows / vSplit);
		// Number of columns per thread (except for the last which gets all the rest)
		int numOfColumnsPerThread = (numOfBigBoardColumns / hSplit);
		
		//num of extra rows for threads in the last row
		int numOfExtraRows = (numOfBigBoardRows % vSplit);
		//num of extra columns for threads in the last column
		int numOfExtraColumns = (numOfBigBoardColumns % hSplit);
		
		// Final board to be written to by the threads
		boolean[][] finalBoard = new boolean[numOfBigBoardRows][numOfBigBoardColumns];
		
		// Counter
		Counter counter = new Counter(vSplit * hSplit);
		
		
		// Outer queues of all threads
		
		SynchronizedQueue<Update>[][] allOuterQueues = new SynchronizedQueue[vSplit][hSplit];
		for (int i=0; i<vSplit; i++) {
			for (int j=0; j<hSplit; j++) {
				allOuterQueues[i][j] = new SynchronizedQueue<Update>();
			}
		}
		
		// Create thread for each devision
		for (int i=0; i<vSplit; i++) {
			for (int j=0; j<hSplit; j++) {
				// find rows that the thread is incharge of
				int rowStart = i * numOfRowsPerThread;
				int rowEnd = rowStart + numOfRowsPerThread - 1;
				if (i == vSplit - 1) { //if this is the thread is in the last row
					rowEnd += numOfExtraRows;
				}
				
				int columnStart = j * numOfColumnsPerThread;
				int columnEnd = columnStart + numOfColumnsPerThread - 1;
				if (j == hSplit - 1) {
					columnEnd += numOfExtraColumns;
				}
				
				// create personal queues array - queues of all neighboring arrays
				SynchronizedQueue<Update>[][] queues = new SynchronizedQueue[3][3]; //TODO: check this!
				for (int k = 0; k < 3; k++) {
					for (int l = 0; l < 3; l++) {
						if ((i + k - 1 < 0) || (i + k - 1 >= vSplit) || (j + l - 1 < 0) || (j + l - 1 >= hSplit)) {
							queues[k][l] = null;
						} else {
							queues[k][l] = allOuterQueues[i + k - 1][j + l - 1];
						}
					}
				}
				
				threads[i][j] = new GOLThread(initalField, numOfBigBoardRows, numOfBigBoardColumns, rowStart, rowEnd, columnStart, columnEnd, queues, finalBoard, generations, counter);
			}
		}
		
		
		
		//start threads
		for (int i=0; i<vSplit; i++) {
			for (int j=0; j<hSplit; j++) {
				threads[i][j].start();
			}
		}

		// Check if threads are done
		counter.waitUntilDone();
		return finalBoard;
	}

}
